/** \file gminiGraph.h
* Header file for a graph basic structures and traversals
*/

#ifndef _GMINI_H
#define _GMINI_H
#include <map>
#include <string>
#include <vector>

using namespace std;
/** \enum NodeType
* enumerates the different types of nodes.
*/
enum NodeType {
/*statements: */ START, EMPTY_ST, ASSIGN_ST, DECL, IF_ST, WHILE_ST, OUTPUT_ST,
/*operators:*/ ARITH_OP, BOOL_OP, BOOL_COMP, ARRAY_OP, PAIR_OP,
/*composite types:*/ ARRAY_TYPE, PAIR_TYPE,
/*leaf nodes:*/ ID_NAME, INT_VAL, BOOL_VAL, INT_TYPE, BOOL_TYPE,
/*error:*/ ERROR_NODE,
LAST_NODE_TYPE};

/** \enum Label
* enumerates the different types of edges.
*/
enum Label {LEFT, RIGHT, VARNAME, VARTYPE, ARRAYSIZE, CONDITION, 
		IF_STATEMENTS, ELSE_STATEMENTS, WHILE_STATEMENTS, 
		NEXT_STATEMENT, INDEX, OF_EDGE /*for array and output*/, LAST_LABEL_TYPE};

/** \enum Direction
* enumerates the different edges directions.
*/
enum Direction {UP, DOWN};

/**
*\brief checks the current node type.
*
*\return a string describing the node type
*/
inline string NodeType2string(NodeType t)
{
	static string typeNodesType[LAST_NODE_TYPE+1]=
	{/*statements: */"Start Virtual Node", "Empty Statement", "Assignment","Declaration","IF Statement","WHILE Statement", "OUTPUT",
	/*operators:*/ "Arithmetic Operation", "BOOLEAN Operation", "BOOLEAN Comparison", "ARRAY Operator", "PAIR Operator",
	/*composite types:*/ "ARRAY", "PAIR",
	/*leaf nodes:*/ "IDENTIFIER", "Integer Value", "Bool Value", "INT", "BOOL",
	/*error:*/ "ERROR",
	"<ERROR>" // LAST_NODE_TYPE must never be tested bc it is just a virtual type
	};
	return typeNodesType[t];
}

/**
*\brief checks the current edge label.
*
*\return a string describing the edge label
*/
inline string Label2string(Label label)
{ 	
	static string typeLabelsType[LAST_LABEL_TYPE+1]= 
		{"Left", "Right","Variable Name", "Variable Type","Array Size",
		 "Condition", "If Statements", "Else Statements", "While Statements",
		  "Next Statement", "Index", "OF",
		  "<ERROR>"};// LAST_LABEL_TYPE must never be tested bc it is just a virtual type
	return typeLabelsType[label];
}

/**
* \class Node
* \author Jad Makhlouta & Layale Constantine
* \brief defines the node structure
*
*/
class Node
{
public:
	NodeType t;//!< Type of node
	string info;//!<Brief Description of node
	/**
	*\brief checks the current node type.
	*
	*\return a string describing the node type
	*/
	string getNodeType()
	{
		return NodeType2string(t);
	}

	/**
	*\brief joins the node type and its specific information in one string.
	*
	*\return a string of the format: "(" getNodeType() ")" Info
	*/
	string print();

	/**
	*\brief initializes a node instance of a specific NodeType and information.
	*
	*/
	Node(NodeType type/*!<NodeType value*/,string text/*!<Specific Information*/):t(type),info(text) {}
};
/**
* \class Edge
* \author Jad Makhlouta & Layale Constantine
* \brief defines the edge structure
*
*/
class Edge
{
public:
	Node * start; //!<Start node of edge
	Node * end; //!<End node of edge
	Label label; //!<Label of edge
	Direction dir; //!<Direction of edge
	
	/**
	*\brief checks the current edge label.
	*
	*\return a string describing the edge label
	*/
	string getLabel()
	{
		return Label2string(label);
	}

	/**
	*\brief initializes an edge instance with a specific start node, end node, label and direction.
	*
	*/
	Edge(Node * s/*!<Start node of edge*/, Node * e/*!<End node of edge*/, Label l/*!<Label of edge*/, Direction d/*!<Direction of edge*/):start(s),end(e),label(l),dir(d) {}
};

/** \enum Type
* enumerates the different types of variables.
*/
enum Type {t_INT, t_BOOL, t_PAIR, t_ARRAY, t_NOT_DEFINED};
typedef vector<Type> TypeList;

typedef multimap< Node *, Edge *> EdgeMap;
typedef pair< Node *, Edge *> MapPair;
typedef map<string, TypeList> VariableMap;

/**
* \class GminiGraph
* \author Layale Constantine & Jad Makhlouta
* \brief defines the initial graph structure used for g-mini parsing
*
*/
class GminiGraph
{
private:
	EdgeMap edge_map;//<! A hash table containing pairs of nodes and edges
	Node * root; //<! The root of our graph structure
	friend class Traversal;
	
public:
	VariableMap variables;//<! A hash table of variables name along with their types which could be simple or composite.

	GminiGraph(){}
	GminiGraph(Node * root)
	{
		this->root=root;
	}
	/*
	* \brief sets the root of g-mini graph structure
	*
	*/
	void setRoot(Node * root/*<!The graph root*/)
	{
		this->root=root;
	}
	
	/*
	* \brief returns the root of g-mini graph structure
	*
	*/
	Node * getRoot()
	{
		return root;
	}
	
	/*
	*\brief  creates an edge with a specific label between a Parent node and Child node with a down directon
	* and another edge with the same label between a Child node and a Parent node with an up direction.
	*/
	void createEdge(Node * Parent/*!<Parent node*/, Node * Child/*!<Child node*/, Label l/*!<Edge label*/)
	{
		edge_map.insert(MapPair(Parent,new Edge(Parent, Child,l, DOWN)));
		edge_map.insert(MapPair(Child,new Edge(Child, Parent,l, UP)));
	}
	
	/**
	*\brief deletes a given node and all its children recursively.
	*
	*/
	void deleteSubTree(Node *n/*the node where to start deletion*/);
	
	
	/**
	*\brief returns the child of a given parent node and an edge label
	*
	*/
	Node * getChild (Node * parent/*<!parent node*/, Label l/*the edge label between the parent and the child*/)
	{
		for (EdgeMap::iterator it=edge_map.find(parent);it!=edge_map.end() && (*it).first==parent;it++)
		{
			if (it->second->dir==DOWN)
			{
				if (it->second->end!=NULL)
				{
					if (it->second->label==l)
						return it->second->end;
				}
			}
		}
		return NULL;
	}
	
	/**
	*\brief returns the parent of a given node
	*
	*/
	Node * getParent (Node * child/*child node*/)
	{
		for (EdgeMap::iterator it=edge_map.find(child);it!=edge_map.end() && (*it).first==child;it++)
			if (it->second->dir==UP)
				if (it->second->end!=NULL)
					return it->second->end;
		return NULL;
	}
	
	/**
	*\brief returns the edge to the parent node of a given child node
	*
	*/
	Edge * getParentEdge (Node * child/*<!child node*/)
	{
		for (EdgeMap::iterator it=edge_map.find(child);it!=edge_map.end() && (*it).first==child;it++)
			if (it->second->dir==UP)
				if (it->second->end!=NULL)
					return it->second;
		return NULL;
	}
	
	/**
	*\brief returns the edge between two nodes
	*
	*/
	Edge * getEdge(Node * parent/*<!parent node*/, Node * child/*<!child node*/)
	{
		for (EdgeMap::iterator it=edge_map.find(parent);it!=edge_map.end() && (*it).first==parent;it++)
			if (it->second->dir==DOWN)
				if (it->second->end==child)
					return it->second;
		return NULL;
	}
};

class Visitor;

/**
* \class Traversal
* \author Jad Makhlouta & Layale Constantine 
* \brief defines the class traversal structure for g-mini graph
*
*/
class Traversal
{
private:
	Visitor * v;//<! graph nodes and edges visitor
	GminiGraph * g;//<! g-mini graph
	
	void traverseGraph_helper(Node * startingNode);
public:
	Traversal(GminiGraph & graph, Visitor & visit): v(&visit), g(&graph) {}
	
	/**
	*\brief establishes a preorder graph traversal from start node with a customized visit function.
	*
	*/
	void operator()();
};


#endif
